package com.qianfeng.iot.sdk.service.impl;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.dc3.common.bean.driver.AttributeInfo;
import com.dc3.common.bean.driver.DriverRegister;
import com.dc3.common.constant.CommonConstant;
import com.dc3.common.model.*;
import com.dc3.common.utils.Dc3Util;
import com.qianfeng.iot.sdk.bean.driver.DriverContext;
import com.qianfeng.iot.sdk.bean.driver.DriverProperty;
import com.qianfeng.iot.sdk.service.DriverMetadataService;
import com.qianfeng.iot.sdk.service.DriverService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.concurrent.*;

/**
 * Created by Jackiechan on 2022/7/26 16:14
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Service
public class DriverMetadataServiceImpl implements DriverMetadataService {

    private DriverContext driverContext;

    @Autowired
    public void setDriverContext(DriverContext driverContext) {
        this.driverContext = driverContext;
    }

    private DriverService driverService;

    @Autowired
    public void setDriverService(DriverService driverService) {
        this.driverService = driverService;
    }

    @Value("${spring.application.name}")
    private String serviceName;

    @Value("${server.port}")
    private int port;

    /**
     * 由驱动的实现在配置文件中将属性配置好,然后会自动封装到这个对象中,包括驱动的名字,类型,相关的位号信息都在里面
     */
    private DriverProperty driverProperty;

    @Autowired
    public void setDriverProperty(DriverProperty driverProperty) {
        this.driverProperty = driverProperty;
    }

    @Override
    public void init() {
        //创建驱动对象
        //   System.err.println(driverProperty);
        //转成驱动对象
        Driver driver = new Driver(driverProperty.getName(), serviceName, Dc3Util.localHost(), port, driverProperty.getType());
        //将驱动进行注册(保存到数据库,但是是由另外一个服务实现的)
        //将数据发到另外一个服务上面,考虑是用http还是异步消息来实现
        //我们在注册之前应该先考虑握个手
        syncHandShake(driver);
        //使用DriverEvent 嵌套DriverRegister的原因是因为我们会有多个不同类型的事件,会携带不同类型的数据,为了方便区分,才进行了嵌套
        driverService
                .driverEventSender(new DriverEvent(//发送一个驱动的事件,将驱动注册到我们的程序中
                        serviceName//服务的名字,也就是当前的驱动是通过哪个程序启动起来的
                        , CommonConstant.Driver.Event.DRIVER_REGISTER//发送的事件类型是驱动注册
                        , new DriverRegister(//驱动注册要填写的信息
                        driverProperty.getTenant()//属于什么租户
                        , driver//驱动的元数据是什么
                        , driverProperty.getDriverAttribute()//驱动的属性有什么
                        , driverProperty.getPointAttribute()//驱动的位号数据有什么
                )));

        //上面的操作仅仅是注册驱动,还需要同步驱动的元数据

        syncDriverMetadata(driver);
        ///我们需要再次发送一个消息,搞manager,大哥你把数据都给我发一份,我有用
        //到时候manager会通过消息再给我们返回一个数据(不需要我们等待)我们通过接收这个mq的数据处理保存起来就可以了
        //System.err.println(driver);
    }

    @Override
    public void upsertPointInfo(PointInfo pointInfo) {
        //找到保存PointInfo的地方,将数据添加进去
        String pointId = pointInfo.getPointId();
        String deviceId = pointInfo.getDeviceId();
        String attributeId = pointInfo.getPointAttributeId();
        PointAttribute pointAttribute = driverContext.getDriverMetadata().getPointAttributeMap().get(attributeId);
        if (pointAttribute != null) {
//            //获取当前设备的数据
//            Map<String, Map<String, AttributeInfo>> map = driverContext.getDriverMetadata().getPointInfoMap().get(deviceId);
//            //如果没有数据
//            if (map == null) {
//                //添加数据
//                map = new ConcurrentHashMap<>();
//                driverContext.getDriverMetadata().getPointInfoMap().put(deviceId, map);
//            }
//                Map<String, AttributeInfo> infoMap = map.get(pointId);
//                if (infoMap == null) {
            //           infoMap = new HashMap<>();
//                    map.put(pointId,infoMap );
//                }
//               infoMap.put(pointAttribute.getName(),new AttributeInfo(pointAttribute.getValue(),pointAttribute.getType()))
//
//
            driverContext.getDriverMetadata().getPointInfoMap()//获取到map
                    .computeIfAbsent(deviceId, key -> new ConcurrentHashMap<>())//从这个map中获取deviceId的map,如果没有就用新的map替换掉
                    .computeIfAbsent(pointId, key2 -> new ConcurrentHashMap<>())//同上,但是这个map其实是上面的返回的map
                    .put(pointAttribute.getName(), new AttributeInfo(pointAttribute.getValue(), pointAttribute.getType()));


        }

    }

    @Override
    public void deletePointInfo(PointInfo pointInfo) {
        //找到保存数据的地方,删除
        String pointId = pointInfo.getPointId();
        String deviceId = pointInfo.getDeviceId();
        String attributeId = pointInfo.getPointAttributeId();
        //先看看有没有这个属性
        PointAttribute pointAttribute = driverContext.getDriverMetadata().getPointAttributeMap().get(attributeId);
        if (pointAttribute != null) {
            //删除
            driverContext.getDriverMetadata().getPointInfoMap().computeIfPresent(deviceId, (key1, value1) -> {
                value1.computeIfPresent(pointId, (key2, value2) -> {
                    value2.entrySet().removeIf(next -> next.getKey().equalsIgnoreCase(pointAttribute.getName()));
                    return null;
                });
                return null;
            });

            driverContext.getDriverMetadata().getPointInfoMap().computeIfPresent(deviceId, (key, value) -> {

                return null;
            });
        }

    }



    @Override
    public void upsertProfile(Profile profile) {
        // Add profile point to context
        driverContext.getDriverMetadata().getProfilePointMap().computeIfAbsent(profile.getId(), k -> new ConcurrentHashMap<>(16));
    }

    @Override
    public void deleteProfile(String id) {
        driverContext.getDriverMetadata().getProfilePointMap().entrySet().removeIf(next -> next.getKey().equals(id));
    }

    @Override
    public void upsertDevice(Device device) {
        // Add device to context
        driverContext.getDriverMetadata().getDeviceMap().put(device.getId(), device);
        // Add device driver info to context
        driverContext.getDriverMetadata().getDriverInfoMap().computeIfAbsent(device.getId(), k -> new ConcurrentHashMap<>(16));
        // Add device point info to context
        driverContext.getDriverMetadata().getPointInfoMap().computeIfAbsent(device.getId(), k -> new ConcurrentHashMap<>(16));
    }

    @Override
    public void deleteDevice(String id) {
        driverContext.getDriverMetadata().getDeviceMap().entrySet().removeIf(next -> next.getKey().equals(id));
        driverContext.getDriverMetadata().getDriverInfoMap().entrySet().removeIf(next -> next.getKey().equals(id));
        driverContext.getDriverMetadata().getPointInfoMap().entrySet().removeIf(next -> next.getKey().equals(id));
    }

    @Override
    public void upsertPoint(Point point) {
        // Upsert point to profile point map context
        driverContext.getDriverMetadata().getProfilePointMap().computeIfAbsent(point.getProfileId(), k -> new ConcurrentHashMap<>(16)).put(point.getId(), point);
    }

    @Override
    public void deletePoint(String profileId, String pointId) {
        // Delete point from profile point map context
        driverContext.getDriverMetadata().getProfilePointMap().computeIfPresent(profileId, (k, v) -> {
            v.entrySet().removeIf(next -> next.getKey().equals(pointId));
            return v;
        });
    }

    @Override
    public void upsertDriverInfo(DriverInfo driverInfo) {
        DriverAttribute attribute = driverContext.getDriverMetadata().getDriverAttributeMap().get(driverInfo.getDriverAttributeId());
        if (null != attribute) {
            // Add driver info to driver info map context
            driverContext.getDriverMetadata().getDriverInfoMap().computeIfAbsent(driverInfo.getDeviceId(), k -> new ConcurrentHashMap<>(16))
                    .put(attribute.getName(), new AttributeInfo(driverInfo.getValue(), attribute.getType()));
        }
    }

    @Override
    public void deleteDriverInfo(String deviceId, String attributeId) {
        DriverAttribute attribute = driverContext.getDriverMetadata().getDriverAttributeMap().get(attributeId);
        if (null != attribute) {
            // Delete driver info from driver info map context
            driverContext.getDriverMetadata().getDriverInfoMap().computeIfPresent(deviceId, (k, v) -> {
                v.entrySet().removeIf(next -> next.getKey().equals(attribute.getName()));
                return v;
            });

            // If the driver attribute is null, delete the driver info from the driver info map context
            driverContext.getDriverMetadata().getDriverInfoMap().entrySet().removeIf(next -> next.getValue().size() < 1);
        }
    }




    /**
     * 握手
     *
     * @param driver
     */
    private void syncHandShake(Driver driver) {
        //修改状态为注册中
        driverContext.setDriverStatus(CommonConstant.Status.REGISTERING);
        try {
            CompletableFuture.runAsync(() -> {
                driverService
                        .driverEventSender(new DriverEvent(//发送一个驱动的事件,将驱动注册到我们的程序中
                                serviceName//服务的名字,也就是当前的驱动是通过哪个程序启动起来的
                                , CommonConstant.Driver.Event.DRIVER_HANDSHAKE//发送的事件类型是驱动注册
                                , null
                        ));


            }).get(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
    }


    private void syncDriverMetadata(Driver driver) {
        //应该是注册之后返回
        try {
            CompletableFuture.runAsync(() -> {
                driverService
                        .driverEventSender(new DriverEvent(//发送一个驱动的事件,将驱动注册到我们的程序中
                                serviceName//服务的名字,也就是当前的驱动是通过哪个程序启动起来的
                                , CommonConstant.Driver.Event.DRIVER_METADATA_SYNC//发送的事件类型是驱动注册
                                , serviceName
                        ));

            }).get(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
    }
}
